package com.alibaba.cloud.ddd.domain.model.voyage;

import java.util.Date;

import com.alibaba.cloud.ddd.domain.model.location.Location;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import com.alibaba.cloud.ddd.domain.shared.ValueObject;

/**
 * A carrier movement is a vessel voyage from one location to another.
 */
@Entity(name = "CarrierMovement")
@Table(name = "CarrierMovement")
public final class CarrierMovement implements ValueObject<CarrierMovement> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2024523672895847749L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public long id;

	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = "arrival_location_id", nullable = false)
	public Location arrivalLocation;

	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = "departure_location_id", nullable = false)
	public Location departureLocation;

	@Column(name = "arrival_time", nullable = false)
	public Date arrivalTime;

	@Column(name = "departure_time", nullable = false)
	public Date departureTime;

	// Null object pattern
	public static final CarrierMovement NONE = new CarrierMovement(Location.UNKNOWN, Location.UNKNOWN, new Date(0),
			new Date(0));

	/**
	 * Constructor.
	 *
	 * @param departureLocation location of departure
	 * @param arrivalLocation   location of arrival
	 * @param departureTime     time of departure
	 * @param arrivalTime       time of arrival
	 */
	// TODO make package local
	public CarrierMovement(Location departureLocation, Location arrivalLocation, Date departureTime, Date arrivalTime) {
		// noinspection ObviousNullCheck
		Validate.noNullElements(new Object[] { departureLocation, arrivalLocation, departureTime, arrivalTime });
		this.departureTime = departureTime;
		this.arrivalTime = arrivalTime;
		this.departureLocation = departureLocation;
		this.arrivalLocation = arrivalLocation;
	}

	/**
	 * @return Departure location.
	 */
	public Location departureLocation() {
		return departureLocation;
	}

	/**
	 * @return Arrival location.
	 */
	public Location arrivalLocation() {
		return arrivalLocation;
	}

	/**
	 * @return Time of departure.
	 */
	public Date departureTime() {
		return new Date(departureTime.getTime());
	}

	/**
	 * @return Time of arrival.
	 */
	public Date arrivalTime() {
		return new Date(arrivalTime.getTime());
	}

	@Override
	public boolean equals(final Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;

		final CarrierMovement that = (CarrierMovement) o;

		return sameValueAs(that);
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.departureLocation).append(this.departureTime)
				.append(this.arrivalLocation).append(this.arrivalTime).toHashCode();
	}

	@Override
	public boolean sameValueAs(CarrierMovement other) {
		return other != null && new EqualsBuilder().append(this.departureLocation, other.departureLocation)
				.append(this.departureTime, other.departureTime).append(this.arrivalLocation, other.arrivalLocation)
				.append(this.arrivalTime, other.arrivalTime).isEquals();
	}

	CarrierMovement() {
		// Needed by Hibernate
	}

}
